﻿module CMakeParsing.ExpressionParser.Ast
//    CMake Classifier:  Syntax coloring and highlighting Visual Studio Extension
//    Copyright (C) 2011 Steven Velez
//
//    This file is part of CMake Classifier
//
//    CMake Classifier is free software: you can redistribute it and/or modify
//    it under the terms of the GNU Lesser General Public License as published by
//    the Free Software Foundation, either version 3 of the License, or
//    (at your option) any later version.
//
//    This program is distributed in the hope that it will be useful,
//    but WITHOUT ANY WARRANTY; without even the implied warranty of
//    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//    GNU Lesser General Public License for more details.
//
//    You should have received a copy of the GNU Lesser General Public License
//    along with this program.  If not, see <http://www.gnu.org/licenses/>.

type Expr =
    | Ident of string
    | Val of System.Double
    | Multi of Expr * Expr
    | Div of Expr * Expr
    | Plus of Expr * Expr
    | Minus of Expr * Expr

type ClassResult =
    | StringBegin of int * int * string * obj
    | StringEnd of int * int * string * obj
    | ParamsBegin  of int * int * string * obj
    | ParamsEnd of int * int * string * obj
    | NoScope of int * int * string * obj

let testIn id collection =
    List.exists (fun tst -> tst = id ) collection

// Recognized list of CMake Commands
let  commands_a = [
    "add_custom_command"
    "add_custom_target"
    "add_definitions"
    "add_dependencies"
    "add_executable"
    "add_library"
    "add_subdirectory"
    "add_test"
    "aux_source_directory"
    ]
let  commands_b = [
    "break"
    "build_command"
    ]
let  commands_c = [
    "cmake_minimum_required"
    "cmake_policy"
    "configure_file"
    "create_test_sourcelist"
    ]
let  commands_d = [
    "define_property"
    ]
let  commands_e = [
    "else"
    "elseif"
    "enable_language"
    "enable_testing"
    "endforeach"
    "endfunction"
    "endif"
    "endmacro"
    "endwhile"
    "execute_process"
    "export"
    ]
let  commands_f = [
    "file"
    "find_file"
    "find_library"
    "find_package"
    "find_path"
    "find_program"
    "fltk_wrap_ui"
    "foreach"
    "function"
    ]
let  commands_g = [
    "get_cmake_property"
    "get_directory_property"
    "get_filename_component"
    "get_property"
    "get_source_file_property"
    "get_target_property"
    "get_test_property"
    ]
//let  commands_h = []
let  commands_i = [
    "if"
    "include"
    "include_directories"
    "include_external_msproject"
    "include_regular_expression"
    "install"
    ]
//let  commands_j = []
//let  commands_k = []
let  commands_l = [
    "link_directories"
    "list"
    "load_cache"
    "load_command"
    ]
let  commands_m = [
    "macro"
    "mark_as_advanced"
    "math"
    "message"
    ]
//let  commands_n = []
let  commands_o = [
    "option"
    "output_required_files"
    ]
let  commands_p = [
    "project"
    ]
let  commands_q = [
    "qt_wrap_cpp"
    "qt_wrap_ui"
    ]
let  commands_r = [
    "remove_definitions"
    "return"
    ]
let  commands_s = [
    "separate_arguments"
    "set"
    "set_directory_properties"
    "set_property"
    "set_source_files_properties"
    "set_target_properties"
    "set_tests_properties"
    "site_name"
    "source_group"
    "string"
    ]
let  commands_t = [
    "target_link_libraries"
    "try_compile"
    "try_run"
    ]
let  commands_u = [
    "unset"
    ]
let  commands_v = [
    "variable_watch"
    ]
let  commands_w = [
    "while"
    ]
//let  commands_x = []
//let  commands_y = []
//let  commands_z = []

let isCommand (id:string) =
    let idLow = id.ToLower()
    match idLow.Chars(0) with
    | 'a' -> testIn idLow commands_a
    | 'b' -> testIn idLow commands_b
    | 'c' -> testIn idLow commands_c
    | 'd' -> testIn idLow commands_d
    | 'e' -> testIn idLow commands_e
    | 'f' -> testIn idLow commands_f
    | 'g' -> testIn idLow commands_g
//    | 'h' -> testIn idLow commands_h
    | 'i' -> testIn idLow commands_i
//    | 'j' -> testIn idLow commands_j
//    | 'k' -> testIn idLow commands_k
    | 'l' -> testIn idLow commands_l
    | 'm' -> testIn idLow commands_m
//    | 'n' -> testIn idLow commands_n
    | 'o' -> testIn idLow commands_o
    | 'p' -> testIn idLow commands_p
    | 'q' -> testIn idLow commands_q
    | 'r' -> testIn idLow commands_r
    | 's' -> testIn idLow commands_s
    | 't' -> testIn idLow commands_t
    | 'u' -> testIn idLow commands_u
    | 'v' -> testIn idLow commands_v
    | 'w' -> testIn idLow commands_w
//    | 'x' -> testIn idLow commands_x
//    | 'y' -> testIn idLow commands_y
//    | 'z' -> testIn idLow commands_z
    | _ -> false

// Recognized list of cmake command "operators"
let  operators_AT = [
    "@ONLY"
    ]
let  operators_A = [
    "ABSOLUTE"
    "AFTER"
    "ALL"
    "ALPHABET"
    "AND"
    "APPEND"
    "ARCHIVE"
    "ARGS"
    "ASCII"
    "AUTHOR_WARNING"
    ]
let  operators_B = [
    "BEFORE"
    "BOOL"
    "BRIEF_DOCS"
    "BUNDLE"
    ]
let  operators_C = [
    "CACHE"
    "CACHED_VARIABLE"
    "CLEAR"
    "CMAKE_FIND_ROOT_PATH_BOTH"
    "CMAKE_FLAGS"
    "CODE"
    "COMMAND"
    "COMMAND_NAME"
    "COMMENT"
    "COMPARE"
    "COMPILE_DEFINITIONS"
    "COMPILE_OUTPUT_VARIABLE"
    "COMPILE_RESULT_VAR"
    "COMPONENT"
    "COMPONENTS"
    "CONFIGS"
    "CONFIGURATION"
    "CONFIGURATIONS"
    "CONFIGURE"
    "COPYONLY"
    "COPY_FILE"
    ]
let  operators_D = [
    "DEFINED"
    "DEFINITION"
    "DEPENDS"
    "DESTINATION"
    "DIRECTORY"
    "DIRECTORY_PERMISSIONS"
    "DOC"
    "DOWNLOAD"
    ]
let  operators_E = [
    "ENV"
    "EQUAL"
    "ERROR_FILE"
    "ERROR_QUIET"
    "ERROR_STRIP_TRAILING_WHITESPACE"
    "ERROR_VARIABLE"
    "ESCAPE_QUOTES"
    "EXACT"
    "EXCLUDE"
    "EXCLUDE_FROM_ALL"
    "EXISTS"
    "EXPECTED_MD5"
    "EXPORT"
    "EXPR"
    "EXT"
    "EXTRA_INCLUDE"
    ]
let  operators_F = [
    "FATAL_ERROR"
    "FILE"
    "FILEPATH"
    "FILES"
    "FILES_MATCHING"
    "FILE_PERMISSIONS"
    "FIND"
    "FOLLOW_SYMLINKS"
    "FORCE"
    "FRAMEWORK"
    "FULL_DOCS"
    "FUNCTION"
    ]
let  operators_G = [
    "GET"
    "GLOB"
    "GLOBAL"
    "GLOB_RECURSE"
    "GREATER"
    ]
let  operators_H = [
    "HEX"
    "HINTS"
    ]
let  operators_I = [
    "IMPLICIT_DEPENDS"
    "INCLUDE_S"
    "INHERITED"
    "INPUT_FILE"
    "INSERT"
    ""
    "IS_ABSOLUTE"
    "IS_DIRECTORY"
    "IS_NEWER_THAN"
    "IS_SYMLINK"
    ]
//let  operators_J = []
//let  operators_K = []
let  operators_L = [
    "LENGTH"
    "LENGTH_MAXIMUM"
    "LENGTH_MINIMUM"
    "LESS"
    "LIBRARY"
    "LIMIT"
    "LIMIT_COUNT"
    "LIMIT_INPUT"
    "LIMIT_OUTPUT"
    "LINK_INTERFACE_LIBRARIES"
    "LOG"
    ]
let  operators_M = [
    "MACOSX_BUNDLE"
    "MAIN_DEPENDENCY"
    "MAKE_DIRECTORY"
    "MATCH"
    "MATCHALL"
    "MATCHES"
    "MODULE"
    ]
let  operators_N = [
    "NAME"
    "NAMELINK_ONLY"
    "NAMELINK_SKIP"
    "NAMES"
    "NAMESPACE"
    "NAME_WE"
    "NEW"
    "NEWLINE_CONSUME"
    "NOT"
    "NOTEQUAL"
    "NO_CMAKE_BUILDS_PATH"
    "NO_CMAKE_ENVIRONMENT_PATH"
    "NO_CMAKE_FIND_ROOT_PATH"
    "NO_CMAKE_PACKAGE_REGISTRY"
    "NO_CMAKE_PATH"
    "NO_CMAKE_SYSTEM_PATH"
    "NO_DEFAULT_PATH"
    "NO_HEX_CONVERSION"
    "NO_MODULE"
    "NO_POLICY_SCOPE"
    "NO_SYSTEM_ENVIRONMENT_PATH"
    ]
let  operators_O = [
    "OFFSET"
    "OLD"
    "ONLY_CMAKE_FIND_ROOT_PATH"
    "OPTIONAL"
    "OR"
    "OUTPUT"
    "OUTPUT_FILE"
    "OUTPUT_QUIET"
    "OUTPUT_STRIP_TRAILING_WHITESPACE"
    "OUTPUT_VARIABLE"
    ]
let  operators_P = [
    "PACKAGE"
    "PARENT_SCOPE"
    "PATH"
    "PATHS"
    "PATH_SUFFIXES"
    "PATTERN"
    "PERMISSIONS"
    "POP"
    "POST_BUILD"
    "PRIVATE_HEADER"
    "PROGRAM"
    "PROGRAMS"
    "PROGRAM_ARGS"
    "PROJECT_NAME"
    "PROPERTIES"
    "PROPERTY"
    "PUBLIC_HEADER"
    "PUSH"
    ]
let  operators_Q = [
    "QUIET"
    ]
let  operators_R = [
    "RANDOM"
    "RANDOM_SEED"
    "READ"
    "READ_WITH_PREFIX"
    "REALPATH"
    "REGEX"
    "REGULAR_EXPRESSION"
    "RELATIVE"
    "RELATIVE_PATH"
    "REMOVE"
    "REMOVE_AT"
    "REMOVE_DUPLICATES"
    "REMOVE_ITEM"
    "REMOVE_RECURSE"
    "RENAME"
    "REPLACE"
    "REQUIRED"
    "RESOURCE"
    "RESULT_VAR"
    "RESULT_VARIABLE"
    "REVERSE"
    "RUNTIME"
    "RUN_OUTPUT_VARIABLE"
    "RUN_RESULT_VAR"
    ]
let  operators_S = [
    "SCRIPT"
    "SEND_ERROR"
    "SET"
    "SHARED"
    "SHOW_PROGRESS"
    "SORT"
    "SOURCE"
    "SOURCES"
    "STATIC"
    "STATUS"
    "STREQUAL"
    "STRGREATER"
    "STRING"
    "STRINGS"
    "STRIP"
    "STRLESS"
    "SUBSTRING"
    "SYSTEM"
    ]
let  operators_T = [
    "TARGET"
    "TARGETS"
    "TEST"
    "TIMEOUT"
    "TOLOWER"
    "TOUPPER"
    "TO_CMAKE_PATH"
    "TO_NATIVE_PATH"
    ]
let  operators_U = [
    "UNIX_COMMAND"
    "USE_SOURCE_PERMISSIONS"
    ]
let  operators_V = [
    "VARIABLE"
    "VERBATIM"
    "VERSION"
    "VERSION_EQUAL"
    "VERSION_GREATER"
    "VERSION_LESS"
    ]
let  operators_W = [
    "WARNING"
    "WIN32"
    "WINDOWS_COMMAND"
    "WORKING_DIRECTORY"
    "WRITE"
    ]
let  operators_d = [
    "debug"
    ]
let  operators_g = [
    "general"
    ]
let  operators_o = [
    "optimized"
    ]

let internal isCommandOperator (id:string) =
    match id.Chars(0) with
    | '@' -> testIn id operators_AT
    | 'A' -> testIn id operators_A
    | 'B' -> testIn id operators_B
    | 'C' -> testIn id operators_C
    | 'D' -> testIn id operators_D
    | 'E' -> testIn id operators_E
    | 'F' -> testIn id operators_F
    | 'G' -> testIn id operators_G
    | 'H' -> testIn id operators_H
    | 'I' -> testIn id operators_I
//    | 'J' -> testIn id operators_J
//    | 'K' -> testIn id operators_K
    | 'L' -> testIn id operators_L
    | 'M' -> testIn id operators_M
    | 'N' -> testIn id operators_N
    | 'O' -> testIn id operators_O
    | 'P' -> testIn id operators_P
    | 'Q' -> testIn id operators_Q
    | 'R' -> testIn id operators_R
    | 'S' -> testIn id operators_S
    | 'T' -> testIn id operators_T
    | 'U' -> testIn id operators_U
    | 'V' -> testIn id operators_V
    | 'W' -> testIn id operators_W
    | 'd' -> testIn id operators_d
    | 'g' -> testIn id operators_g
    | 'o' -> testIn id operators_o
    | _ -> false